package rctgl.park.sv4;

import java.util.Random;

import javax.media.opengl.GL;

import com.sun.opengl.util.texture.*;

import panoramic.textures.DefaultTextures;
import panoramic.geometry.*;
import panoramic.PanoScene;

import rctgl.park.RCTGLStatics;
import rctgl.park.RCTGLTextures;
import rctgl.park.RCTSerializable;
import rctgl.park.sv4.*;
import rctgl.ICompilable;

public class SV4Surface extends SV4Element implements RCTSerializable
{
	private int m_surfaceType, m_edgeType;
	private int m_waterHeight;
	private int m_slopeType;
	private int m_grassState = GrassState.CLEAR;
	
	
	private boolean m_isColored = false;
	
	private double [] m_heightOffsets; 
	private double m_TR, m_TL, m_BL, m_BR;
	
	private boolean m_compiledSurface=false, m_compiledWater=false;
	
	
	public double getTR()
	{
		return m_TR;
	}
	
	public double getTL()
	{
		return m_TL;
	}
	
	public double getBL()
	{
		return m_BL;
	}
	
	public double getBR()
	{
		return m_BR;
	}
	
	public boolean hasWater()
	{
		return m_waterHeight > m_baseHeight;
		
	}
	
	public int getSurfaceType()
	{
		return m_surfaceType;
	}
	
	public int getEdgeType()
	{
		return m_edgeType;
	}
	
	public int getSlopeType()
	{
		return m_slopeType;
	}
	
	public int getWaterHeight()
	{
		return m_waterHeight;
	}
	
	
	public boolean isSurfaceCompiled()
	{
		return m_compiledSurface;
	}
	
	public void setIsSurfaceCompiled(boolean val)
	{
		m_compiledSurface = val;
	}
	
	public boolean isWaterCompiled()
	{
		return m_compiledWater;
	}
	
	public void setIsWaterCompiled(boolean val)
	{
		m_compiledWater = val;
	}
	
	

	public void loadFromBytes(int[] data)
	{
		//get the surface type
		int extendedSurfaceType = data[0] & 0x01; //(data[0] & 0x08) >> 8;
		int extendedEdgeType = (data[0] & 0x80) >> 8;
		
		int baseSurfaceType = (data[5] & 0xE0) >> 5;
		
		if(extendedSurfaceType != 0 && m_isLL)
		{
			switch(baseSurfaceType)
			{
			case 0:
				m_surfaceType = SurfaceTypes.SNOW;
				break;
			case 1:
				m_surfaceType = SurfaceTypes.LOG_ROOF;
				break;
			case 2:
				m_surfaceType = SurfaceTypes.IRON_ROOF;
				break;
			case 3:
				m_surfaceType = SurfaceTypes.GREY_ROOF;
				break;
			case 4:
				m_surfaceType = SurfaceTypes.RED_GRID;
				break;
			case 5:
				m_surfaceType = SurfaceTypes.YELLOW_GRID;
				break;
			case 6:
				m_surfaceType = SurfaceTypes.PURPLE_GRID;
				break;
			case 7:
				m_surfaceType = SurfaceTypes.GREEN_GRID;
				break;
			}
		}
		else
		{
			switch(baseSurfaceType)
			{
			case 0:
				m_surfaceType = SurfaceTypes.GRASS;
				break;
			case 1:
				m_surfaceType = SurfaceTypes.SAND;
				break;
			case 2:
				m_surfaceType = SurfaceTypes.DIRT;
				break;
			case 3:
				m_surfaceType = SurfaceTypes.ROCK;
				break;
			case 4:
				m_surfaceType = SurfaceTypes.MARTIAN;
				break;
			case 5:
				m_surfaceType = SurfaceTypes.CHECKERBOARD;
				break;
			case 6:
				m_surfaceType = SurfaceTypes.JUNGLE;
				break;
			case 7:
				m_surfaceType = SurfaceTypes.ROOFING;
				break;
			
			}
		}
		
		
		loadHeights(data);
		
		//top 3 bits of 5th byte are edge type
		m_edgeType = (extendedEdgeType << 4) | ((data[4] & 0xE0) >> 5);
		
		//bottom 5 bits of 5th byte are slope type
		m_slopeType = (data[4] & 0x1F);
		calculateHeightFromSlopeType();
		
		
		//bottom 5 bits of 6th bytes is water height
		m_waterHeight = (data[5] & 0x1F) * 4;
		
		
		
		
		
		
		
	}
	
	private void calculateHeightFromSlopeType()
	{
		m_heightOffsets = new double[4];
		
		if(m_slopeType == 0)
		{
			m_BL = m_BR = m_TR = m_TL = m_baseHeight;
			return;
		}
		
		int off = 4;
		
		// 5th bit is set for pieces that go up 2 units
		if((m_slopeType & 0x10) != 0)
		{
			m_maxHeight += 2*off;
			
			if((m_slopeType & 0x01) == 0)
			{
				m_heightOffsets[2] = 0*off;
				m_heightOffsets[3] = 1*off;
				m_heightOffsets[1] = 1*off;
				m_heightOffsets[0] = 2*off;
			}
			else if((m_slopeType & 0x04) == 0)
			{
				m_heightOffsets[2] = 2*off;
				m_heightOffsets[3] = 1*off;
				m_heightOffsets[1] = 1*off;
				m_heightOffsets[0] = 0*off;
			}
			
			else if((m_slopeType & 0x02) == 0)
			{
				m_heightOffsets[3] = 0*off;
				m_heightOffsets[1] = 2*off;
				m_heightOffsets[0] = 1*off;
				m_heightOffsets[2] = 1*off;
			}
			else if((m_slopeType & 0x08) == 0)
			{
				m_heightOffsets[3] = 2*off;
				m_heightOffsets[1] = 0*off;
				m_heightOffsets[0] = 1*off;
				m_heightOffsets[2] = 1*off;
			}
			
			
		}
		else
		{
			m_maxHeight += 1*off;
			
			if((m_slopeType & 0x01) != 0)
				m_heightOffsets[2] = 1*off;
			if((m_slopeType & 0x02) != 0)
				m_heightOffsets[3] = 1*off;
			if((m_slopeType & 0x04) != 0)
				m_heightOffsets[0] = 1*off;
			if((m_slopeType & 0x08) != 0)
				m_heightOffsets[1] = 1*off;
			
		}
		
		m_BL = m_baseHeight + m_heightOffsets[0];
		m_BR = m_baseHeight + m_heightOffsets[1];
		m_TR = m_baseHeight + m_heightOffsets[2];
		m_TL = m_baseHeight + m_heightOffsets[3];
		
	}

	public byte[] writeToBytes(byte[] srcData)
	{
		return null;
	}
	
	
	public boolean extendsWater(SV4Surface origSurf, int xOff, int yOff)
	{
		return m_waterHeight == origSurf.getWaterHeight();
	}
	
	public void compileWater(PanoScene scene, int xLen, int zLen)
	{
		if(!hasWater())
			return;
		
		PanoPolygon poly = new PanoPolygon();
		
		poly.setTextureID( RCTGLTextures.WATER_SURFACE );
		double targetHeight = m_waterHeight  * RCTGLStatics.UNIT_HEIGHT;
		
		
		Random rand = new Random();
		
		
		poly.setRGB( new PanoRGB( rand.nextDouble(), rand.nextDouble(), rand.nextDouble()) );		
		//poly.setRGBA( new PanoRGBA(1, 1, 1, 0.5d) );
		
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					targetHeight,
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, 0));
		poly.addVertex( new PanoVertex3(
					(m_xCoord+xLen) * RCTGLStatics.UNIT_WIDTH,
					targetHeight,
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(xLen, 0));
		poly.addVertex( new PanoVertex3(
					(m_xCoord+xLen) * RCTGLStatics.UNIT_WIDTH,
					targetHeight,
					(m_yCoord+zLen) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(xLen, zLen));
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					targetHeight,
					(m_yCoord+zLen) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, zLen));
			
		
		
		
		scene.addObject(poly);
		
	}

	public void compileSurface(PanoScene scene, int xLen, int zLen)
	{
		PanoPolygon poly = new PanoPolygon();
		
		
		double targetHeight = m_baseHeight  * RCTGLStatics.UNIT_HEIGHT;
		
		
		switch(m_surfaceType)
		{
		case SurfaceTypes.CHECKERBOARD:
			poly.setTextureID( RCTGLTextures.CHECKERBOARD_SURFACE );
			break;
		case SurfaceTypes.DIRT:
			poly.setTextureID( RCTGLTextures.DIRT_SURFACE );
			break;
		case SurfaceTypes.GRASS:
			poly.setTextureID( RCTGLTextures.GRASS_SURFACE );
			break;
		case SurfaceTypes.GREEN_GRID:
			poly.setTextureID( RCTGLTextures.GREEN_GRID_SURFACE);
			break;
		case SurfaceTypes.GREY_ROOF:
			poly.setTextureID( RCTGLTextures.GREY_ROOF_SURFACE);
			break;
		case SurfaceTypes.IRON_ROOF:
			poly.setTextureID( RCTGLTextures.IRON_ROOF_SURFACE );
			break;
		case SurfaceTypes.JUNGLE:
			poly.setTextureID( RCTGLTextures.JUNGLE_SURFACE );
			break;
		case SurfaceTypes.LOG_ROOF:
			poly.setTextureID( RCTGLTextures.LOG_ROOF_SURFACE);
			break;
		case SurfaceTypes.MARTIAN:
			poly.setTextureID( RCTGLTextures.MARTIAN_SURFACE );
			break;
		case SurfaceTypes.PURPLE_GRID:
			poly.setTextureID( RCTGLTextures.PURPLE_GRID_SURFACE );
			break;
		case SurfaceTypes.RED_GRID:
			poly.setTextureID( RCTGLTextures.RED_GRID_SURFACE );
			break;
		case SurfaceTypes.ROCK:
			poly.setTextureID( RCTGLTextures.ROCK_SURFACE );
			break;
		case SurfaceTypes.ROOFING:
			poly.setTextureID( RCTGLTextures.ROOFING_SURFACE);
			break;
		case SurfaceTypes.SAND:
			poly.setTextureID( RCTGLTextures.SAND_SURFACE );
			break;
		case SurfaceTypes.SNOW:
			poly.setTextureID( RCTGLTextures.SNOW_SURFACE );
			break;
		case SurfaceTypes.YELLOW_GRID:
			poly.setTextureID( RCTGLTextures.YELLOW_GRID_SURFACE );
			break;
		
		}
			
		Random rand = new Random();
		
		
		//poly.setRGB( new PanoRGB( rand.nextDouble(), rand.nextDouble(), rand.nextDouble()) );
		poly.setRGB( new PanoRGB( 1, 1, 1) );
			
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					targetHeight + (m_heightOffsets[0] * RCTGLStatics.UNIT_HEIGHT),
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, 0));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+xLen) * RCTGLStatics.UNIT_WIDTH,
					targetHeight + (m_heightOffsets[1] * RCTGLStatics.UNIT_HEIGHT),
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(xLen, 0));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+xLen) * RCTGLStatics.UNIT_WIDTH,
					targetHeight + (m_heightOffsets[2] * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+zLen) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(xLen, zLen));
		
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					targetHeight + (m_heightOffsets[3] * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+zLen) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, zLen));
			
		
		
		
		scene.addObject(poly);
				
	}
	
	public boolean isExtendable()
	{
		switch(m_slopeType)
		{
		case 0x00:
			return true;
		}
		
		return false;
	}
	
	public boolean extendsPiece(SV4Surface origSurf, int xOff, int yOff)
	{
		if(m_slopeType != origSurf.getSlopeType() ||
				m_surfaceType != origSurf.getSurfaceType())
			return false;
		
		
		if(m_slopeType == 0)
		{
			return( m_baseHeight == origSurf.getBaseHeight());
		}
		
		return false;
		
		
		
	}
	
	private static void setEdgeTexture(PanoPolygon poly, SV4Surface surface)
	{
		switch(surface.getEdgeType())
		{
		case EdgeTypes.BLACK_WALL:
			poly.setTextureID( RCTGLTextures.BLACK_WALL_EDGE );
			return;
		case EdgeTypes.BRICK:
			poly.setTextureID( RCTGLTextures.BRICK_EDGE );
			return;
		case EdgeTypes.BROWN_STONE:
			poly.setTextureID( RCTGLTextures.BROWN_STONE_EDGE );
			return;
		case EdgeTypes.GREEN_WALL:
			poly.setTextureID( RCTGLTextures.GREEN_WALL_EDGE );
			return;
		case EdgeTypes.GREY_STONE:
			poly.setTextureID( RCTGLTextures.GREY_STONE_EDGE );
			return;
		case EdgeTypes.GREY_WALL:
			poly.setTextureID( RCTGLTextures.GREY_WALL_EDGE );
			return;
		case EdgeTypes.ICE:
			poly.setTextureID( RCTGLTextures.ICE_EDGE );
			return;
		case EdgeTypes.IRON:
			poly.setTextureID( RCTGLTextures.IRON_EDGE );
			return;
		case EdgeTypes.PURPLE_WALL:
			poly.setTextureID( RCTGLTextures.PURPLE_WALL_EDGE );
			return;
		case EdgeTypes.RED_WALL:
			poly.setTextureID( RCTGLTextures.RED_WALL_EDGE );
			return;
		case EdgeTypes.ROCK:
			poly.setTextureID( RCTGLTextures.ROCK_EDGE );
			return;
		case EdgeTypes.SKYSCRAPER_A:
			poly.setTextureID( RCTGLTextures.SKYSCRAPER_A_EDGE );
			return;
		case EdgeTypes.SKYSCRAPER_B:
			poly.setTextureID( RCTGLTextures.SKYSCRAPER_B_EDGE );
			return;
		case EdgeTypes.WOOD:
			poly.setTextureID( RCTGLTextures.WOOD_EDGE );
			return;
		case EdgeTypes.YELLOW_WALL:
			poly.setTextureID( RCTGLTextures.YELLOW_WALL_EDGE );
			return;
		
		
		
		
		
		
		
		
		
		
		
		
		
		}
		
	}
	
	
	public void compileNorthEdge(PanoScene scene, SV4Surface otherSurface)
	{
		if(getTR() == otherSurface.getBR() && getTL() == otherSurface.getBL())
			return;
		
		SV4Surface targetSurface = this;
		if(otherSurface.getBR() >= getTR() && otherSurface.getBL() >= getTL())
			targetSurface = otherSurface;
		
		PanoPolygon poly = new PanoPolygon();
		
		setEdgeTexture(poly, targetSurface);
		
		
		Random rand = new Random();
		
		
		//poly.setRGB( new PanoRGB( rand.nextDouble(), rand.nextDouble(), rand.nextDouble()) );
		poly.setRGB( new PanoRGB( 1, 1, 1) );
			
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					(getTL() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, (getTL()/4)));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(getTR() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(1, (getTR()/4)));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(otherSurface.getBR() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(1, (otherSurface.getBR()/4)));
		
		poly.addVertex( new PanoVertex3(
					m_xCoord * RCTGLStatics.UNIT_WIDTH,
					(otherSurface.getBL() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, (otherSurface.getBL()/4)));
			
		
		
		
		scene.addObject(poly);
		
	}
	
	public void compileEastEdge(PanoScene scene, SV4Surface otherSurface)
	{
		if(getTR() == otherSurface.getTL() && getBR() == otherSurface.getBL())
			return;
		
		SV4Surface targetSurface = this;
		if(otherSurface.getTL() >= getTR() && otherSurface.getBL() >= getBR())
			targetSurface = otherSurface;
		
		
		PanoPolygon poly = new PanoPolygon();
		
		setEdgeTexture(poly, targetSurface);
		
		
		Random rand = new Random();
		
		
		//poly.setRGB( new PanoRGB( rand.nextDouble(), rand.nextDouble(), rand.nextDouble()) );
		poly.setRGB( new PanoRGB( 1, 1, 1) );
			
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(getBR() * RCTGLStatics.UNIT_HEIGHT),
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, (getBR()/4)));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(getTR() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(1, (getTR()/4)));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(otherSurface.getTL() * RCTGLStatics.UNIT_HEIGHT),
					(m_yCoord+1) * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(1, (otherSurface.getTL()/4)));
		
		poly.addVertex( new PanoVertex3(
					(m_xCoord+1) * RCTGLStatics.UNIT_WIDTH,
					(otherSurface.getBL() * RCTGLStatics.UNIT_HEIGHT),
					m_yCoord * RCTGLStatics.UNIT_WIDTH),
				new PanoVertex2(0, (otherSurface.getBL()/4)));
			
		
		
		
		scene.addObject(poly);
		
	}
}
